/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.quangtin3;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.log4j.Logger;

/**
 * Bouncing two socket. Transfer all comming data from sock_from to sock_to
 * @author quangtin3@gmai.com
 */
public class Bouncing implements Runnable {

    private static AtomicLong threadId = new AtomicLong(0);
    private Socket sock_from,  sock_to;
    private Logger log;
    private static int BOUNCE_BUFFER_SIZE = 16384;

    public Bouncing(Socket sock_from, Socket sock_to, Logger log) {
        if (sock_from == null || sock_to == null || log == null) {
            throw new NullPointerException("socket or log in construct null");
        }

        this.sock_from = sock_from;
        this.sock_to = sock_to;
        this.log = log;

        Thread t = new Thread(this, "BOUNCING " + threadId.incrementAndGet());
        t.start();

        log.info("bounce thread from " + sock_from + " to " + sock_to + "started");

    }

    @Override
    public String toString() {
        if (sock_from == null | sock_to == null) {
            return "bounce thread not ready";
        } else {
            return "bounce thread from " + sock_from.getInetAddress().getHostAddress() +
                    " to " + sock_to.getInetAddress().getHostAddress();
        }
    }

    public void run() {
        try {
            InputStream in = sock_from.getInputStream();
            OutputStream out = sock_to.getOutputStream();

            byte[] buff = new byte[BOUNCE_BUFFER_SIZE];
            int len;    //read length

            while ((len = in.read(buff)) != -1) {
                out.write(buff, 0, len);
                log.debug(this + ": transfer " + len + " bytes");
            }

        } catch (Exception e) {
            log.error(this + ": bouncing error", e);
        } finally {
            try {
                sock_from.close();
                sock_to.close();
                log.info(this + ": close both socket");
            } catch (IOException e) {
                log.warn(this + ": close socket error", e);
            }
        }

    }
}
